package com.hjy.backend.dataManager.dataItem;

import com.hjy.backend.commons.SubArray;
import com.hjy.backend.dataManager.DataManagerImpl;
import com.hjy.backend.dataManager.page.Page;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * DataItem 是 DM 层向上层提供的数据抽象。上层模块通过地址，向 DM 请求到对应的 DataItem，再获取到其中的数据。
 * <p>
 * DataItem 中保存的数据，结构如下：
 * [ValidFlag] [DataSize] [Data]
 * <p>
 * 其中 ValidFlag 占用 1 字节，标识了该 DataItem 是否有效。
 * 删除一个 DataItem，只需要简单地将其有效位设置为 0。
 * DataSize 占用 2 字节，标识了后面 Data 的长度。
 * <p>
 * 在上层模块试图对 DataItem 进行修改时，需要遵循一定的流程：
 * <p>
 * 在修改之前需要调用 before() 方法，
 * <p>
 * 想要撤销修改时，调用 unBefore() 方法，
 * <p>
 * 在修改完成后，调用 after() 方法。
 * <p>
 * 整个流程，主要是为了保存前相数据，并及时落日志。DM 会保证对 DataItem 的修改是原子性的。
 */
public class DataItemImpl implements DataItem {
    static final int OF_VALID = 0;
    static final int OF_SIZE = 1;
    static final int OF_DATA = 3;

    private SubArray raw;
    private byte[] oldRaw;
    private Lock rLock; // 读锁
    private Lock wLock; // 写锁
    private DataManagerImpl dm;
    private long uid; // 左边4位表示 pageID , 右边4位表示offset偏移
    private Page pg;

    public DataItemImpl(SubArray raw, byte[] oldRaw, Page pg, long uid, DataManagerImpl dm) {
        this.raw = raw;
        this.oldRaw = oldRaw;
        ReadWriteLock lock = new ReentrantReadWriteLock();
        rLock = lock.readLock();
        wLock = lock.writeLock();
        this.dm = dm;
        this.uid = uid;
        this.pg = pg;
    }

    public boolean isValid() {
        return raw.raw[raw.start + OF_VALID] == (byte) 0;
    }

    @Override
    public SubArray data() {
        return new SubArray(raw.raw, raw.start + OF_DATA, raw.end);
    }

    @Override
    public void before() {
        wLock.lock();
        pg.setDirty(true);
        System.arraycopy(raw.raw, raw.start, oldRaw, 0, oldRaw.length);
    }

    @Override
    public void unBefore() {
        System.arraycopy(oldRaw, 0, raw.raw, raw.start, oldRaw.length);
        wLock.unlock();
    }

    @Override
    public void after(long xid) {
        // 写入日志
        dm.logDataItem(xid, this);
        wLock.unlock();
    }

    @Override
    public void release() {
        dm.releaseDataItem(this);
    }

    @Override
    public void lock() {
        wLock.lock();
    }

    @Override
    public void unlock() {
        wLock.unlock();
    }

    @Override
    public void rLock() {
        rLock.lock();
    }

    @Override
    public void rUnLock() {
        rLock.unlock();
    }

    @Override
    public Page page() {
        return pg;
    }

    @Override
    public long getUid() {
        return uid;
    }

    @Override
    public byte[] getOldRaw() {
        return oldRaw;
    }

    @Override
    public SubArray getRaw() {
        return raw;
    }
}
